package com.tianquan.miracle.beans;/**
 * @author starsea
 * @date 2024-09-28 下午10:17
 */

import com.tianquan.miracle.conditional.parseConditionPostProcessor;
import com.tianquan.miracle.core.RegisterBean;
import com.tianquan.miracle.postprocessor.BeanPostProcessor;
import com.tianquan.miracle.postprocessor.InitializingBean;
import com.tianquan.miracle.postprocessor.InjectBeanPostProcessor;
import com.tianquan.miracle.util.MiracleUtils;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @BelongsProject: uploaddemo
 * @BelongsPackage: com.tianquan.miracle.beans
 * @Author: tianquan
 * @CreateTime: 2024-09-28  22:17
 * @Description: bean注册器
 * @Version: 1.0
 */
public abstract class AbstractBeanRegister extends AbstractCommonBeanProcessor implements RegisterBean {

    // 一级缓存
    private final Map<String, Object> beanCache = new HashMap<>();

    // 二级缓存
    private final Map<String, Object> secondCache = new HashMap<>();

    // 普通的bean定义对象
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();


    @Override
    public void registerBean(String name, Object bean) {
        beanCache.put(name, bean);
    }

    public void loadBean() {
        for (String beanName : beanDefinitionMap.keySet()) {
            getSingletonBean(beanName);
        }
    }

    protected Object getSingletonBean(String name) {
        // 查询一级缓存
        Object bean = beanCache.get(name);
        if (bean == null) {
            // 查询二级缓存
            if ((bean = secondCache.get(name)) != null) return bean;
        } else return bean;
        // 缓存中都也没有，则创建bean
        bean = createBean(name);
        // 放入一级缓存
        beanCache.put(name, bean);
        secondCache.remove(name);
        return bean;
    }

    protected <T> T getSingletonBean(Class<T> aClass) {
        // 查询一级缓存
        for (Map.Entry<String, Object> stringObjectEntry : beanCache.entrySet()) {
            Object bean = stringObjectEntry.getValue();
            if (MiracleUtils.isRelatedTo(bean.getClass(), aClass)) {
                return (T) bean;
            }
        }
        // 查询二级缓存
        for (Map.Entry<String, Object> stringObjectEntry : secondCache.entrySet()) {
            Object bean = stringObjectEntry.getValue();
            if (MiracleUtils.isRelatedTo(bean.getClass(), aClass)) {
                return (T) bean;
            }
        }
        return (T) createBean(MiracleUtils.createBeanName(aClass));
    }

    public Object createBean(String name) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if (beanDefinition == null) return null;
        // 实例化
        Object bean = instantiateBean(beanDefinition);
        // 放入二级缓存
        secondCache.put(name, bean);
        // 填充属性
        populateBean(bean);
        // 初始化
        bean = initializeBean(bean, name);
        return bean;
    }

    // 实例化
    public Object instantiateBean(BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getaClass();
        try {
            // 默认使用无参构造
            Constructor<?> constructor = beanClass.getConstructor();
            return constructor.newInstance();
        } catch (Exception e) {
            Constructor<?> agrsConstructor;
            // 如果没有无参构造，则使用有参构造
            Constructor<?>[] constructors = beanClass.getConstructors();
            // 有参构造大于1个就不知道用哪个,抛出异常
            if (constructors.length > 1) throw new RuntimeException("beanClass has more than one constructor");
            // 拿到有参构造,并获取参数需要的值
            agrsConstructor = constructors[0];
            Class<?>[] parameterTypes = agrsConstructor.getParameterTypes();
            int length = parameterTypes.length;
            Object[] args = new Object[length];
            for (int i = 0; i < length; i++) {
                args[i] = getSingletonBean(parameterTypes[i]);
            }
            try {
                // 使用有参构造实例化对象
                return agrsConstructor.newInstance(args);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    // 属性填充
    public void populateBean(Object bean) {

        for (InjectBeanPostProcessor injectBeanPostProcessor : injectBeanPostProcessors) {
            injectBeanPostProcessor.inject(bean);
        }

        for (parseConditionPostProcessor parseConditionPostProcessor : parseConditionPostProcessors) {
            parseConditionPostProcessor.parseCondition(bean);
        }

    }

    // 初始化
    public Object initializeBean(Object bean, String name) {
        // Aware接口
        invokeAwareMethods(bean, name);

        // Bean的前置初始化方法
        if (bean instanceof BeanPostProcessor beanPostProcessor) {
            try {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, name);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        // 初始化
        invokeInitMethods(bean, name);

        // Bean的后置初始化方法
        if (bean instanceof BeanPostProcessor beanPostProcessor) {
            try {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, name);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        // 是否有必要创建对象的代理类
        return isNecessaryCreateProxy(bean);
    }


    private void invokeInitMethods(Object bean, String name) {
        if (bean instanceof InitializingBean initializingBean) {
            try {
                initializingBean.afterPropertiesSet();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public abstract void invokeAwareMethods(Object bean, String name);

    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(name, beanDefinition);
    }

    public BeanDefinition getBeanDefinition(String name) {
        return beanDefinitionMap.get(name);
    }

    public List<BeanDefinition> getAllBeanDefinition() {
        return new ArrayList<>(beanDefinitionMap.values());
    }

    public void clean() {
        beanCache.clear();
        secondCache.clear();
        beanDefinitionMap.clear();
    }

    public <T> List<T> selectBean(Class<T> beanClass) {
        return beanCache.values().stream()
                .filter(bean -> beanClass.isAssignableFrom(bean.getClass()))
                .map(bean -> (T) bean)
                .collect(Collectors.toList());
    }

    protected boolean containsValue(Class<?> aClass) {
        return beanCache.values().stream().map(Object::getClass).anyMatch(aClass::isAssignableFrom);
    }

    protected boolean containsKey(String name) {
        return beanDefinitionMap.containsKey(name);
    }

}
